<?php
namespace Lavender;

abstract class ARouter {
	
	protected static $_router = null;
	protected $current_path = null;
	protected $current_verb = null;
	protected $_rules = [];
	
	protected $config = ['init'=>true];
	
	abstract protected function init();
	
	private function __construct($config = [])
	{
	    $this->config = array_merge($this->config,$config);
	    if(isset($this->config['init']) && $this->config['init'])
	    {
		    $this->init();
	    }
	}
	
	protected static function getInstance($config = [])
	{
		if(empty(self::$_router))
		{
			self::$_router = new static($config);
		}
		return self::$_router;
	}
	
	public static function getRequestVerb()
	{
		return strtolower(isset($_SERVER['REQUEST_METHOD'])?$_SERVER['REQUEST_METHOD']:'GET');
	}
	
	public static function getUrlPath()
	{
		$uri = isset($_SERVER['REQUEST_URI'])?$_SERVER['REQUEST_URI']:'';
		$pos = strpos($uri, '?');
		if($pos !== false)
		{
			return substr($uri, 0,$pos);
		}
		return $uri;
	}
	
	/**
	 * 检查路由
	 */
	public static function check($url_path=null,$verb=null)
	{
		$router = self::getInstance();
		$router->current_path = null;
		$router->current_verb = null;
		if(empty($url_path))
		{
		    $url_path = self::getUrlPath();
		}
		return $router->checkRules($url_path,$verb);
	}
	
	/**
	 * 规则测试
	 */
	protected function checkRules($url_path,$verb=null)
	{
	    if(empty($verb))
	    {
		    $verb =  self::getRequestVerb();
	    }
		$path = $this->checkRulesVerb($url_path, $verb);
		if(!$path)
		{
		   $verb = 'any';
		   $path = $this->checkRulesVerb($url_path, $verb);
		   if(!$path)
		   {
		       return false;
		   }
		}
		$this->current_verb = $verb;
		$this->current_path = $path;
		return true;
	}
	
	protected function checkRulesVerb($url_path,$verb)
	{
	    if(isset($this->_rules[$verb]))
	    {
	        $path = $this->filterPath($url_path,$this->_rules[$verb]);
	        if($path)
	        {
	            return $path;
	        }
	        else if(isset($this->_rules[$verb]['regexs']) && count($this->_rules[$verb]['regexs']) > 0)
	        {
	            return $this->filterRegex($url_path,$this->_rules[$verb]['regexs'],$verb);
	        }
	    }
	    else 
	    {
	        return false;
	    }
	}
	
	public static function getModule()
	{
		throw new \Exception('need overwrite!');
	}
	
	public static function getAction()
	{
		throw new \Exception('need overwrite!');
	}
	
	public static function getAlias()
	{
		throw new \Exception('need overwrite!');
	}

	/**
	 * 获取路由中的数据
	 */
	public static function getData($key)
	{
		$router = self::getInstance();
		if(!isset($router->_rules[$router->current_verb][$router->current_path]['datas'][$key]))
		{
			return null;
		}
		return $router->_rules[$router->current_verb][$router->current_path]['datas'][$key];
	}
	
	/**
	 * 获取路由中的全局数据
	 */
	public static function getGdata($key)
	{
		$router = self::getInstance();
		if(!isset($router->_rules[$router->current_verb][$router->current_path]['gdatas'][$key]))
		{
			return null;
		}
		return $router->_rules[$router->current_verb][$router->current_path]['gdatas'][$key];
	}
	
	/**
	 * 路由注入
	 */
	public static function __callStatic($name,$args)
	{
		$verbs = ['any','get','head','post','put','patch','delete','options'];
		if(in_array($name,$verbs))
		{
			array_unshift($args, $name);
			return call_user_func_array([self::getInstance(),'addRouteToList'], $args);
		}
		else
		{
			throw new \Exception("method '{$name}' does not found!");
		}
	}
	
	/**
	 * 添加路由表
	 */
	protected function addRouteToList($verb,$url_path,$configure)
	{
		$res = self::parseAndAdd($verb,$url_path,$configure);
	}
	
	public static function parseAndAdd ($verb, $url_path, $configure)
    {
        $rule = null;
        $router = self::getInstance();
        $is_filter = self::isFilterUrl($url_path);
        if ($is_filter) {
            $rule = self::parseRegex($url_path, $configure);
        } else {
            $rule = self::parsePath($url_path, $configure);
        }
        $url_path = $rule['rule'];
        unset($rule['rule']);
        $router->_rules[$verb][$url_path] = $rule;
        if ($is_filter) {
            $router->_rules[$verb]['regexs'][] = $url_path;
        }
    }

    public static function parsePath ($url_path, $configure)
    {
        $config_data = self::parseConfigure($configure);
        return [
                'rule' => $url_path,
                'gdatas' =>$config_data,
        ];
    }
	
	public static function parseRegex($url_path, $configure) 
	{
		$match = self::getMatch($url_path );
		$url_path = $match ['preg'];
		$config_data = self::parseConfigure ($configure);
		$config_data ['keys'] = $match ['names'];
		return [ 
				'rule' => $url_path,
				'gdatas' => $config_data,
		];
	}
	
	public static function parseConfigure($configure)
	{
		$to = isset($configure['uses']) ? $configure['uses']: '';
		$to_array = explode('@', $to);
		if(count($to_array) != 2)
		{
			throw new \Exception("route configure '{$to}'  [uses] is a Error Format; Format must be like 'module@action'!");
		}
		$res = [];
		$res['module'] = $to_array[0];
		$res['action'] = $to_array[1];
		$res['alias'] = isset($configure['as'])?$configure['as']:$to;
		return $res;
	}
	
	public static function getMatch($url_match)
	{
		$match['preg'] = '';
		$match['names'] = [];
		$from = '/{(\w+)\:\\\\([dw])}/';
		$to = '(\\\\$2+)';
		$result = [];
		$ret = @preg_match_all($from, $url_match,$result);
		if($ret)
		{
			$match['names'] = $result[1];
			$match['preg'] = preg_replace($from, $to, $url_match);
			$match['preg'] = "/^".str_replace(['/','-','.'], ['\\/','\\-','\\.'], $match['preg'])."$/";
		}
		else
		{
			return $match['preg'] = $url_match;
		}
		return $match;
	}
	
	public static function isFilterUrl($url_path)
	{
		if(strlen($url_path)<4)
		{
			return false;
		}
		//标准正则
		if($url_path[0] == "/" && $url_path[1] == "^")
		{
			return true;
		}
		//内部定义的正则
		$d_pos = strpos($url_path, ":\\d}");
		$w_pos = strpos($url_path, ":\\w}");
		if($d_pos||$w_pos)
		{
			return true;
		}
		return false;
	}
	
	
	protected function filterPath($url_path,$filters)
	{
		$ret = false;	
		foreach($filters as $filter => $other)
		{
			if($filter == 'regexs') 
			{
			    continue;
			}
			if ($filter === $url_path) {
			   $ret =  $filter;
			   break;
			}
		}
		return $ret;
	}
	
	protected function filterRegex($url_path,$filters,$verb)
	{
	    $ret = false;
	    $router = self::getInstance();
	    foreach($filters as $filter)
	    {
	        $res = [];
	        $tmp = @preg_match($filter, $url_path,$res);	
	        if(!$tmp)
	        {
	            continue;
	        }  
	        array_shift($res);	
	        if(isset($router->_rules[$verb][$filter]['gdatas']['keys']))
	        {
	            $keys = $router->_rules[$verb][$filter]['gdatas']['keys'];
	            if(count($keys) == count($res))
	            {
	                $router->_rules[$verb][$filter]['datas'] = array_combine($keys, $res);
	            }
	        }
	        $ret = $filter;
	        break;
	    }
	    return $ret;
	}	
}


class Router extends ARouter
{
	public static function getModule()
	{
		return self::getGdata('module');
	}

	public static function getAction()
	{
		return self::getGdata('action');
	}

	public static function getAlias()
	{
		return self::getGdata('alias');
	}


	protected function init()
	{
		$this->loadRules();
	}

	protected function LoadRules()
	{
	    $path = L_APP_PATH.'conf/route_compiled.php';
	    $this->_rules = $this->LoadRuleFile($path);
	}
	
	public function LoadRuleFile($path)
	{
	    return include_once $path;
	}
	
	public static function getRules($path)
	{
	    $route = self::getInstance(['init'=>false]);
	  
	    $route->LoadRuleFile($path);
	    return $route->_rules;
	}

}
